import { complement, curry, lensPath, partition, view } from "ramda";
import { HealthDescEnum } from "../model/v3pro.enum";
import {
  V3proCommunication,
  V3proHeath,
  V3proScreenAllStatus,
  V3proSendBox,
  V3proSendBoxOutPort,
  V3proSendBoxScanBoard,
  V3proSendBoxScanBoardPanel,
  V3proSta,
} from "../../../api/v3pro-api/v3pro.api.response.model";
import { PanelGroupKey } from "../model/v3pro.model";

interface HealthStatus {
  status: V3proHeath;
}

interface CommunicationStatus {
  status: V3proCommunication;
}

/**
 * V3proSendboxLambda
 * 用于v3pro发送盒的 lambada 表达式封装，用于通用处理
 */
export class V3proAllStatusLambda {
  /**
   * 健康描述焦点
   */
  private static readonly healthDescriptionWithStatusLens = lensPath<
    HealthStatus,
    HealthDescEnum[]
  >(["status", "healthDesc", "description"]);

  /**
   * 异常匹配
   */
  static readonly healthDescrWithStatusEquals = curry(
    <T extends HealthStatus>(description: HealthDescEnum | HealthDescEnum[], target: T) => {
      const t = view(V3proAllStatusLambda.healthDescriptionWithStatusLens, target) || [];
      if (Array.isArray(description)) {
        return t.some((t) => description.includes(t));
      } else {
        return t.includes(description);
      }
    },
  ) as (description: HealthDescEnum | HealthDescEnum[]) => (target: HealthStatus) => boolean;

  /**
   * 健康描述焦点
   * @private
   */
  private static readonly healthDescriptionLens = lensPath<V3proHeath, HealthDescEnum[]>([
    "healthDesc",
    "description",
  ]);

  /**
   * 不带 status的 健康状态
   */
  static readonly healthDescriptionEquals = curry(
    <T extends V3proHeath>(description: HealthDescEnum | HealthDescEnum[], target: T) => {
      const t = view(V3proAllStatusLambda.healthDescriptionLens, target) || [];
      if (Array.isArray(description)) {
        return t.some((t) => description.includes(t));
      } else {
        return t.includes(description);
      }
    },
  ) as (description: HealthDescEnum | HealthDescEnum[]) => (target: V3proHeath) => boolean;

  /**
   * 通讯状态焦点
   */
  static readonly communicationStatusLens = lensPath<CommunicationStatus, string>([
    "status",
    "communication",
    "control",
    "status",
  ]);

  /**
   * 通讯状态是否为OK
   */
  static readonly communicationOK = (target: CommunicationStatus) => {
    return view(V3proAllStatusLambda.communicationStatusLens, target) === "OK";
  };

  /**
   * 过滤通讯状态和网络状态
   * @param target
   */
  static readonly commAndNetOk = (target: V3proSendBox): boolean => {
    return (
      V3proAllStatusLambda.communicationOK(target) &&
      complement(V3proAllStatusLambda.healthDescrWithStatusEquals(HealthDescEnum.COMM_NODE_ERR))(
        target,
      )
    );
  };

  /**
   * 将扫描框的坐标和面板的坐标进行修正
   * 1. 将相对偏移转换为绝对偏移
   * @param allStatus
   */
  static readonly accAllStatusXY = (allStatus: V3proScreenAllStatus) => {
    if (allStatus.isAbsolute) return allStatus;
    allStatus.sendBoxs.forEach((sendBox) => {
      const { x: bx, y: by } = sendBox.layout;
      const scanBoardBaseXY: any[] = [];
      sendBox.layout.outPorts.forEach((port) => {
        port.sendBoxIp = sendBox.addr;
        port.x += bx;
        port.y += by;
        const { x: px, y: py } = port;
        port.scanBoards.forEach((board) => {
          board.x = px;
          board.y = py;
          scanBoardBaseXY.push(board);
        });
      });
      sendBox.scanBoards.forEach((scanBoard) => {
        const scanBoardId = scanBoard.deviceInfo.id;
        const boards = scanBoardBaseXY.filter((board) => board.id === scanBoardId);
        const badPixels = scanBoard?.status?.badPixel?.panelBadPixelArr || [];
        scanBoard.layout.x += boards[0].x;
        scanBoard.layout.y += boards[0].y;
        boards.forEach((b) => {
          b.x = scanBoard.layout.x;
          b.y = scanBoard.layout.y;
        });
        const { x: sx, y: sy } = scanBoard.layout;
        const { width: scanWidth, height: scanHeight } = scanBoard.layout;
        if (!scanBoard.status.panelConf) return;
        const { width, height } = scanBoard.status.panelConf;
        const column = Math.floor(scanWidth / width);
        scanBoard.status.panels.forEach((panel) => {
          panel.x = (panel.index % column) * width + sx;
          panel.y = Math.floor(panel.index / column) * height + sy;
          panel.panelBadPixels = badPixels.find((bp) => bp.panelIndex === panel.index) || {};
        });
      });
    });
    allStatus.isAbsolute = true;
    return allStatus;
  };

  /**
   * 发送盒过滤
   * @param allStatus
   * @param filter
   * @param remove
   */
  static readonly filterSendBoxesWithRemove = (
    allStatus: V3proScreenAllStatus,
    filter: (sendBox: V3proSendBox) => boolean,
    remove = true,
  ) => {
    this.accAllStatusXY(allStatus);
    const [errs, normal] = partition(filter, allStatus.sendBoxs);
    if (remove) allStatus.sendBoxs = normal;
    return [errs, normal];
  };

  /**
   * 筛选发送盒网口
   * @param allStatus
   * @param outPortFilter
   * @param sendBoxFilter
   * @param remove
   */
  static readonly filterSendBoxOutPortNetworkWithRemove = (
    allStatus: V3proScreenAllStatus,
    outPortFilter: (outPort: V3proSta) => boolean,
    sendBoxFilter: (port: V3proSendBox) => boolean = () => true,
    remove = [true, false],
  ) => {
    this.accAllStatusXY(allStatus);
    const ans: V3proSendBoxOutPort[] = [];
    const normals: V3proSendBoxOutPort[] = [];
    const [sendBoxes] = this.filterSendBoxesWithRemove(allStatus, sendBoxFilter, remove[1]);
    sendBoxes.forEach((box) => {
      const cmm = box.status.communication;
      const staList =
        box.layout.outputMode === "net port"
          ? cmm.interfacesSta.filter((is) => is.interfaceIndex > 0)
          : cmm.opticalFibersSta;
      const filterRes = staList;
      const [errs, normal] = partition(outPortFilter, staList);
      const errIdxes = errs.map((item) => item.interfaceIndex - 1);
      const normalOutPorts: V3proSendBoxOutPort[] = [];
      box.layout.outPorts.forEach((port, i) => {
        if (errIdxes.includes(i)) ans.push(port);
        else normalOutPorts.push(port);
      });
      normals.push(...normalOutPorts);
      if (remove[0]) {
        box.layout.outPorts = normalOutPorts;
        const scanBoardsIds = normalOutPorts.reduce((t, port) => {
          port.scanBoards.forEach((sb) => t.push(sb.id));
          return t;
        }, [] as string[]);
        box.scanBoards = box.scanBoards.filter((sb) => scanBoardsIds.includes(sb.deviceInfo.id));
      }
    });
    return [ans, normals];
  };

  /**
   * 扫描过滤
   * 1. 遍历所有扫描版，过滤出错误扫描版
   * 2. 将扫描版中的错误扫描板移除
   * 3. 该函数存在副作用，将修改原始数据
   * @param allStatus
   * @param scanBoardFilter
   * @param sendBoxFilter
   * @param remove
   */
  static readonly filterScanBoardsWithRemove = (
    allStatus: V3proScreenAllStatus,
    scanBoardFilter: (scanBoard: V3proSendBoxScanBoard) => boolean,
    sendBoxFilter: (sendBox: V3proSendBox) => boolean = () => true,
    remove = [true, false],
  ) => {
    this.accAllStatusXY(allStatus);
    const ans: V3proSendBoxScanBoard[] = [];
    const normals: V3proSendBoxScanBoard[] = [];
    const [sendBoxes] = this.filterSendBoxesWithRemove(allStatus, sendBoxFilter, remove[1]);
    sendBoxes.forEach((box) => {
      const [errs, normal] = partition(scanBoardFilter, box.scanBoards);
      normals.push(...normal);
      if (remove[0]) box.scanBoards = normal;
      ans.push(...errs);
    });
    return [ans, normals];
  };

  /**
   * 筛选面板
   * @param allStatus
   * @param panelFilter
   * @param scanBoardFilter
   * @param sendBoxFilter
   * @param remove
   */
  static readonly filterPanelsWithRemove = (
    allStatus: V3proScreenAllStatus,
    panelFilter: (panel: V3proSendBoxScanBoardPanel, scanBoard: V3proSendBoxScanBoard) => boolean,
    scanBoardFilter: (scanBoard: V3proSendBoxScanBoard) => boolean = () => true,
    sendBoxFilter: (sendBox: V3proSendBox) => boolean = () => true,
    remove = [true, false, false],
  ) => {
    this.accAllStatusXY(allStatus);
    const [scanBoards] = this.filterScanBoardsWithRemove(
      allStatus,
      scanBoardFilter,
      sendBoxFilter,
      remove.slice(1),
    );
    const ans: PanelGroupKey[] = [];
    const normals: PanelGroupKey[] = [];
    const filter = (scanBoard: V3proSendBoxScanBoard) => (panel: V3proSendBoxScanBoardPanel) =>
      panelFilter(panel, scanBoard);
    scanBoards.forEach((scanBoard) => {
      const [errs, normal] = partition(filter(scanBoard), scanBoard.status.panels);
      normals.push(PanelGroupKey.build(scanBoard, normal));
      if (remove[0]) scanBoard.status.panels = normal;
      if (errs.length) ans.push(PanelGroupKey.build(scanBoard, errs));
    });
    return [ans, normals];
  };
}
